package com.lifesting.tool.j2as3.visitor;

import java.util.HashSet;
import java.util.List;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.WildcardType;

import com.lifesting.tool.j2as3.action.NodeWriter;
import com.lifesting.tool.j2as3.action.Util;
import com.lifesting.tool.j2as3.action.NodeWriter.Writer;
import com.lifesting.tool.j2as3.impose.ILogger;
import com.lifesting.tool.j2as3.impose.IPersistent;

public class BaseVisitor extends ASTVisitor {
	public static final String LEVEL_KEY = "LEVEL_KEY";
	public static final String TYPE_KEY = "TYPE_KEY";
	public static final String MESSAGE_KEY = "MESSAGE_KEY";

	public static final String POINTER_KEY = "j2as3Container";
	protected NodeWriter nodeWriter;
	protected String[] srcPackages;
	protected IPersistent peristent;
	protected ILogger logger;
	protected int level = 0;
	protected String packageName;
	protected HashSet<String> parameterTypes = new HashSet<String>();
	public BaseVisitor(NodeWriter nodeWriter) {
		super();
		this.nodeWriter = nodeWriter;
	}
	@Override
	public void preVisit(ASTNode node) {
		if (indentCare(node)) {
			++level;
		}
	}

	@Override
	public void postVisit(ASTNode node) {
		if (indentCare(node)) {
			--level;
		}
		nodeWriter.finish(node);
	}
	protected void saveScene(ASTNode node) {
		node.setProperty(LEVEL_KEY, level);
		level = 1;
	}

	protected void restoreScene(ASTNode node) {
		level = ((Integer) node.getProperty(LEVEL_KEY));
	}
	protected boolean indentCare(ASTNode node) {
		if (node instanceof Statement) {
			return !(node instanceof Block);
		} else if (node instanceof BodyDeclaration) {
			return true;
		} else if (node instanceof ImportDeclaration) {
			return true;
		}
		return false;
	}
	
	protected void reset(){
		level = 0;
		
		getNodeWriter().reset();
	}
	public NodeWriter getNodeWriter() {
		return nodeWriter;
	}

	public void setPeristent(IPersistent peristent) {
		this.peristent = peristent;
	}
	public void setLogger(ILogger logger) {
		this.logger = logger;
	}
	public void setSrcPackages(String[] srcPackages) {
		for (String s : srcPackages){
			logger.log("used root package:"+s);
		}
		this.srcPackages = srcPackages;
	}

	protected com.lifesting.tool.j2as3.action.NodeWriter.Writer o(ASTNode node) {
		return nodeWriter.getWriter(node);
	}
	@Override
	public boolean visit(PackageDeclaration node) {
		o(node).println("package " + (packageName = node.getName().getFullyQualifiedName()));
		o(node).println("{");
		return false;
	}
	
	@Override
	public boolean visit(ImportDeclaration node) {
		com.lifesting.tool.j2as3.action.NodeWriter.Writer w = o(node);
		printIndents(w);
		w.println("import "+node.getName().getFullyQualifiedName() + ";");
		return false;
	}
	protected void rightRecursiveVisit(List nodes, Writer wt) {
		int ct = 0;
		for (Object o : nodes) {
			if (ct++ > 0)
				wt.print(", ");
			((ASTNode) o).accept(this);
		}
	}
	protected void visitChildren(List nodes) {
		for (Object o : nodes) {
			((ASTNode) o).accept(this);
		}
	}
	protected void printIndents(Writer w) {
		if (level > 0) {
			w.printTab(level);
		}
	}
	
	protected String getTypeName(Type type) {
		if (type == null) {
			return "";
		} else if (type.isPrimitiveType()) {
			String ptn = ((PrimitiveType)type).getPrimitiveTypeCode().toString();
			if (ptn.equals("byte") || ptn.equals("short")|| ptn.equals("long") || ptn.equals("float")
					|| ptn.equals("double")){
				ptn = "Number";
			}else if(ptn.equals("boolean")){
				ptn = "Boolean";
			}else if (ptn.equals("char")){
				ptn = "String";
			}
			return ptn;
		} else if (type.isSimpleType()) {
			//check whether is it a parameterize type
			Name n = ((SimpleType) type).getName();
			//It must be a simple name if it's be parameterized
			if (n.isSimpleName() && parameterTypes.contains(((SimpleName)n).getIdentifier())){
				return "Object";
			}
			return n.getFullyQualifiedName();
		} else if (type.isQualifiedType()) {
			return ((QualifiedType) type).getName().getFullyQualifiedName();
		} else if (type.isArrayType()) {
			return "Array";
		}else if (type.isParameterizedType()){
			return getTypeName(((ParameterizedType)type).getType());
		}else if(type.isWildcardType()){
			return "Object";
		}else{
			return "!!";
		}
	}
	@Override
	public boolean visit(ArrayType node) {
		o(node).print(getTypeName(node));
		return false;
	}
	@Override
	public boolean visit(ParameterizedType node) {
		o(node).print(getTypeName(node));
		return false;
	}
	@Override
	public boolean visit(PrimitiveType node) {
		o(node).print(getTypeName(node));
		return false;
	}
	@Override
	public boolean visit(QualifiedType node) {
		o(node).print(getTypeName(node));
		return false;
	}
	@Override
	public boolean visit(SimpleType node) {
		return true;
	}
	@Override
	public boolean visit(WildcardType node) {
		o(node).print(getTypeName(node));
		return false;
	}
	@Override
	public boolean visit(Modifier node) {
		if (node.isAbstract()){
			node.getParent().setProperty(Util.IS_ABSTRACT, Boolean.TRUE);
			o(node).print("/*abstract*/");
		}
		if (node.isFinal()){
			if (node.getProperty(BodyVisitor.HIDE) == Boolean.TRUE){
				o(node).print("/*final*/");
			}
			else{
				o(node).print("final ");
			}
		}
		if (node.isPublic()){
			o(node).print("public ");
		}
		if (node.isPrivate()){
			o(node).print("private ");
		}
		if (node.isTransient()){
			o(node).print("/*transient*/");
		}
		if (node.isNative())
		{
			o(node).print("native ");
		}
		return false;
	}
	
}
